The goal of this notebook is to figure out the best way to compare subjects’ gestures with their references. Some questions of interest:

1 Loading PitchTier & IntensityTier files

Using rPraat, load a the Praat PitchTier, Prosogram PitchTier, and IntensityTier of an example file, interpolate to get the f0 and intensity values every 10ms and plot. Comparing at 10 ms intervals comes from d’Alessandro et al 2011.

In 6_compareGesture, I used the JSON versions of the Prosogram pitch tiers, but it’s actually better to directly use the _styl.PitchTier files because rPraat can do interpolation automatically.

library("zoo")
library("rPraat")
library("tidyverse")

path="../data/20_12_15-pilot_gestures/ambiguous/reference/"

# Find intensity files
setwd(paste0(path, "intensity"))
The working directory was changed to /Users/xx/Github/GepetoR/data/20_12_15-pilot_gestures/ambiguous/reference/intensity inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
it_filenames <- dir(pattern="\\.IntensityTier$")

# Find pitchTier files 
setwd("../pitchtier")
pt_filenames <- dir(pattern="\\.PitchTier$")

# Find Prosogram PitchTier files
setwd("../prosogram_pt")
psg_filenames <- dir(pattern="\\.PitchTier$")

# Interpolate pitch and intensity for one phrase and plot
setwd("../../../../../notebooks/")

it1 <- it.read(paste0(path, "intensity/", it_filenames[1]))
it1_interp <- it.interpolate(it1, seq(0, it1$tmax, by=0.01))

pt1 <- pt.read(paste0(path, "pitchtier/", pt_filenames[1]))
pt1_interp <- pt.interpolate(pt1, seq(0, pt1$tmax, by=0.01))

psg1 <- pt.read(paste0(path, "prosogram_pt/", psg_filenames[1]))
psg1_interp <- pt.interpolate(psg1, seq(0, psg1$tmax, by=0.01))

ref_data <- tibble(t=pt1_interp$t, f=pt1_interp$f, psg.f=psg1_interp$f, i=it1_interp$i)

# Plot
name <- str_split(it_filenames[1], '.IntensityTier')[[1]][1]
ggplot(data=ref_data, aes(x=t, y=f)) +
      geom_line(aes(color="Praat Pitch")) +
      scale_color_discrete(name = "Data")  +
      geom_line(aes(x=t, y=i, color="Intensity")) +
      geom_line(aes(x=t, y=psg.f, color="Prosogram Pitch")) +
      labs(title="Interpolated pitch tier and intensity tiers", subtitle=paste("Phrase:",name),
          x="time (seconds)",
          y="Frequency (Hz) for pitch, \nunspecified for intensity")

Interestingly, the Praat Pitch curve and the Prosogram one for this particular phrase differ by way more than I had expected. The pitch goes way too high in Praat’s analysis. I think those are artifacts.

2 Compare original pitch curve with Prosogram stylization

Summary: Use Prosogram stylization. Praat’s pitch analysis has some artifacts and weird microprosody. Weighing by intensity doesn’t seem to make a huge difference, but it doesn’t take so much more work. Also, since d’Alessandro et al 2011 used it, it’s good to be consistent.

Process: Now let’s compare the Praat Pitch curve and Prosogram pitch curve for all the files.

In 6_compareGesture, I arbitrarily picked 100 as the number of data points to compare. Now we can compare every 10ms. This means that the curves for each file has a different number of points.

First, get the interpolated time series values for all the original phrases. The pitch values are converted to semitones with a reference of 130Hz to match the input gestures.

  index<-match(phrase_name, ref_info$phrase)
Error in match(phrase_name, ref_info$phrase) : 
  object 'phrase_name' not found

Now let’s compare the Praat Pitch curve with the Prosogram pitch curve for each phrase:

# Helper function to find the straight up correlation between the praat and prosogram frequency curves
get_corr_praat_psg <- function(gest_name) {
  praat_pitch <- get_ref_tsf(gest_name)
  prosogram_pitch <- get_ref_ptsf(gest_name)
  return(cor(praat_pitch, prosogram_pitch, method="pearson"))
}

corr_praat_psg_refs <- do.call("rbind", lapply(ref_phrases, function(elt) {
  get_corr_praat_psg(elt)
}))[,1]

results_corr_praat_prosogram <- tibble(phrase=ref_phrases, corr.praat.psg=corr_praat_psg_refs)
print(as.matrix(results_corr_praat_prosogram), quote = FALSE)
      phrase                                  corr.praat.psg
 [1,] 1_Il_nait_tres_premature                0.8737483     
 [2,] 10_jean_saigne_beaucoup                 0.9271921     
 [3,] 10bis_jenseigne_beaucoup                0.6056561     
 [4,] 13_la_belle_ferme_le_voile              0.6591321     
 [5,] 13bis_la_belle_ferme__le_voile          0.9229426     
 [6,] 1bis_Il_naitrait_premature              0.6524992     
 [7,] 2_tu_parais_tres_soucieux               0.9301414     
 [8,] 20_jean_pierre_et_jacques               0.9743508     
 [9,] 20bis_jeanpierre_et_jacques             0.7922471     
[10,] 27_la_bonne__cuisine_avec_des_navets    0.9824346     
[11,] 27bis_la_bonne_cuisine__avec_des_navets 0.8148815     
[12,] 2bis_tu_paraitrais_soucieux             0.8566172     
mean(results_wcorr_praat_prosogram$corr.praat.psg)
[1] 0.8326536

Does it make a difference if we weigh by intensity?

library("wCorr") # for the weighted correlation function


get_weighted_corr_praat_psg <- function(phrase_name) {
  praat_pitch <- get_ref_tsf(phrase_name)
  prosogram_pitch <- get_ref_ptsf(phrase_name)
  intensity <- get_ref_tsi(phrase_name)
  
  wc <- weightedCorr(praat_pitch, prosogram_pitch, method = "Pearson", 
               weights = intensity, ML = FALSE, fast = TRUE)
  return(wc)
}

wcorr_praat_psg_refs <- do.call("rbind", lapply(ref_phrases, function(elt) {
  get_weighted_corr_praat_psg(elt)
}))[,1]

results_wcorr_praat_prosogram <- tibble(phrase=ref_phrases, corr.praat.psg=corr_praat_psg_refs,
                                        wcorr.praat.psg=wcorr_praat_psg_refs)
print(as.matrix(results_wcorr_praat_prosogram), quote = FALSE)
      phrase                                  corr.praat.psg wcorr.praat.psg
 [1,] 1_Il_nait_tres_premature                0.8737483      0.8576868      
 [2,] 10_jean_saigne_beaucoup                 0.9271921      0.9333632      
 [3,] 10bis_jenseigne_beaucoup                0.6056561      0.7371725      
 [4,] 13_la_belle_ferme_le_voile              0.6591321      0.7631101      
 [5,] 13bis_la_belle_ferme__le_voile          0.9229426      0.9554644      
 [6,] 1bis_Il_naitrait_premature              0.6524992      0.6093226      
 [7,] 2_tu_parais_tres_soucieux               0.9301414      0.9196042      
 [8,] 20_jean_pierre_et_jacques               0.9743508      0.9680748      
 [9,] 20bis_jeanpierre_et_jacques             0.7922471      0.8534788      
[10,] 27_la_bonne__cuisine_avec_des_navets    0.9824346      0.9802765      
[11,] 27bis_la_bonne_cuisine__avec_des_navets 0.8148815      0.8291139      
[12,] 2bis_tu_paraitrais_soucieux             0.8566172      0.8422050      
mean(results_wcorr_praat_prosogram$wcorr.praat.psg)
[1] 0.8540727

The mean of the weighted correlation is a little higher but not by much.

3 Plotting some more examples


plot_ref_curves <- function(phrase_name) {
  # first get the info
  praat_pitch <- get_ref_tsf(phrase_name)
  prosogram_pitch <- get_ref_ptsf(phrase_name)
  intensity <- get_ref_tsi(phrase_name)
  timestamps <- get_ref_timestamps(phrase_name)
  
  # combine into a tibble
  ref_data <- tibble(f=praat_pitch, i=intensity, psg.f=prosogram_pitch, t=timestamps)
  
  # plot
  ggplot(data=ref_data, aes(x=t, y=f)) +
      geom_point(size=0.5, aes(color="Praat Pitch")) +
      scale_color_discrete(name = "Data")  +
      geom_point(aes(x=t, y=i, color="Intensity"), size=0.5) +
      geom_point(aes(x=t, y=psg.f, color="Prosogram Pitch"), size=0.5) +
      labs(title="Interpolated pitch tier and intensity tiers", subtitle=paste("Phrase:",phrase_name),
          x="time (seconds)",
          y="Frequency (Hz) for pitch, \nunspecified for intensity")  
}

The most different. Here I think Prosogram is more accurate. The part that goes really low and really high in Praat’s pitch curve seem to be artifacts.

plot_ref_curves(ref_phrases[6])

The most similar

plot_ref_curves(ref_phrases[10])

4 Weighted comparison of Prosogram pitch curve with learners’ gestures

Load gestures. Interpolate based on the number of points in the reference gesture. The index indicates time in terms of a percentage from 0 to 1.

library("jsonlite")

# Get all the filenames of the gestures
path="../data/20_12_15-pilot_gestures/ambiguous/gestures/"
setwd(path)
The working directory was changed to /Users/xx/Github/GepetoR/data/20_12_15-pilot_gestures/ambiguous/gestures inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
gest_filenames <- dir(pattern="\\.gest$")
name <- gest_filenames[1]

# Extract the reference phrase from the whole gesture name
get_ref_name <- function(gest_name) {
  return(str_split(gest_name, '-')[[1]][1])
}

# Finds the number of samples in the reference for the particular gesture
get_num_samples <-function(gest_name) {
# Find how many samples are in the ref file
  ref_phrase <- get_ref_name(gest_name)  
  return(length(get_ref_timestamps(ref_phrase)))
}

# Load gest data and convert time
prep_gest<-function(gest) {
  # Convert time to percentage
  max_time <- tail(gest$t_end, 1)
  gest <- gest %>% mutate(percent=t_init/max_time) %>%
             # only keep local scrub time and the freq values 
             select(percent, f, scrub)
  return(gest)
}

# args: data - tibble with columns: percent (% of way through signal) & f (frequency at that point), 
# num_samples - # equally spaced points in outpt
# returns: tibble with coluumns index & value. index has num_samples equally spaced points from 0 to 1
# values are interplolated from f
get_interpolated_data <- function(data, num_samples) {
  # Add end points at 0 and 1 if they don't already exist, duplicating first and last available f value
  if (! 0 %in% data$percent) {
    data <- add_row(data, percent=0, f=data$f[1], scrub=data$scrub[1], .before=1)
  }
  if (! (1 %in% data$percent)) {
    data <- add_row(data, percent=1, f=tail(data$f, 1), scrub=tail(data$scrub, 1))
  }
          
  # Create a tibble with all the points we are interested in, with NA values
  sample_points <- tibble(percent=seq(0, 1, length.out=num_samples), f=NA, scrub=NA) 
  
  # Add all the sample points whose scrub value doesn't already exist in data
  data2 <- bind_rows(data, filter(sample_points, !(percent %in% data$percent)))  %>%
    # Then sort by scrub columns  
    arrange(percent)
  
  # Transform into zoo object to fill the NAs with interpolated values
  z <- read.zoo(data2) %>% na.approx %>%
   tk_tbl(preserve_index=TRUE, rename_index="index") %>%
   filter(index %in% sample_points$percent)
  
  return(z)
}

setwd("../../../../notebooks/")

# A vector with just the names of the phrases (w/o .json extension)
gests <- do.call("rbind", lapply(gest_filenames, function(elt) {
  str_split(elt, '.gest')[[1]][1]
}))[,1]

# Original gestures
gest_originals <- lapply(gest_filenames, function(elt) {
  result <- fromJSON(paste0(path, elt)) %>% as_tibble() %>% 
  # NOTE THIS EXTRA STEP      
  prep_gest()
})

# Returns the index of the gesture
get_gest_index <- function(gest_name) {
  index <- match(gest_name, gests)
  if (is.na(index)) { return(NULL) }
  return(index)
}

# Interpolated time series data of f & scrub value
gest_interps <- lapply(gests, function(elt) {
  # Elt is the name of a gesture. We want to find its index first
  index<-get_gest_index(elt)
  
  # In order to get the actual gesture data
  gest_orig<-gest_originals[[index]]
  
  # Gets number of samples for the gest, which is computed from the reference
  num_samples<-get_num_samples(elt)
  
  # Do the interpolation  
  gest_interp<-get_interpolated_data(gest_orig, num_samples)
  
  return(gest_interp)
})

4.1 Comparing with reference

# Compute the correlation and rms given the entire gesture name
get_corr <- function(gest_name, weights=FALSE) {
  # Get the index of the gesture
  index<-get_gest_index(gest_name)
  # Use it to get the freq of the gesture
  gest_pitch <- gest_interps[[index]]$f
  
  # Get the reference phrase
  phrase_name <- get_ref_name(gest_name)    
  # Use it to get the prosogram pitch ref and the intensity
  prosogram_pitch <- get_ref_ptsf(phrase_name)
  
  # If using weights, get the intensity of the ref phrase
  if (weights) {  
    intensity <- get_ref_tsi(phrase_name)
  
    wc <- weightedCorr(gest_pitch, prosogram_pitch, method = "Pearson", 
               weights = intensity, ML = FALSE, fast = TRUE)
    return(wc)
  } else {
    return(weightedCorr(gest_pitch, prosogram_pitch, method = "Pearson", 
               weights = rep(1, length(gest_pitch)), ML = FALSE, fast = TRUE))
  }
}

# Get the weighted correlation
wcorr_all <- do.call("rbind", lapply(gests, function(elt) {
  get_corr(elt, weights=TRUE)
}))[,1]
# And the normal correlation just for comparison
corr_all <- do.call("rbind", lapply(gests, function(elt) {
  get_corr(elt)
}))[,1]

gest_scores <- tibble(gest=gests, wcorr=wcorr_all, corr=corr_all) %>%
                mutate(corr_diff=wcorr-corr)
print(as.matrix(gest_scores), quote = FALSE)
      gest                                       wcorr       corr        corr_diff   
 [1,] 1_Il_nait_tres_premature-a_c                0.42554949  0.49247005 -0.066920559
 [2,] 1_Il_nait_tres_premature-b_m                0.70046009  0.76441071 -0.063950613
 [3,] 10_jean_saigne_beaucoup-a_m                -0.24744923 -0.14092195 -0.106527278
 [4,] 10bis_jenseigne_beaucoup-l1                 0.18463382  0.41247045 -0.227836635
 [5,] 10bis_jenseigne_beaucoup-l2                 0.62856787  0.74169334 -0.113125467
 [6,] 13_la_belle_ferme_le_voile-c1               0.74861175  0.75599321 -0.007381461
 [7,] 13_la_belle_ferme_le_voile-c2               0.92952515  0.93418818 -0.004663033
 [8,] 13bis_la_belle_ferme__le_voile-g1           0.87004356  0.85569850  0.014345053
 [9,] 13bis_la_belle_ferme__le_voile-g2           0.93840305  0.93579395  0.002609107
[10,] 1bis_Il_naitrait_premature-a_y             -0.28209329 -0.26562495 -0.016468340
[11,] 1bis_Il_naitrait_premature-b_g              0.90009583  0.85585444  0.044241390
[12,] 2_tu_parais_tres_soucieux-a_c               0.69232721  0.72664280 -0.034315591
[13,] 2_tu_parais_tres_soucieux-b_s               0.90859845  0.90619253  0.002405921
[14,] 20_jean_pierre_et_jacques-s1               -0.19066244 -0.04477046 -0.145891986
[15,] 20_jean_pierre_et_jacques-s2                0.36546810  0.56495981 -0.199491709
[16,] 20_jean_pierre_et_jacques-s3                0.44503271  0.61057645 -0.165543739
[17,] 20bis_jeanpierre_et_jacques-y1              0.01394261 -0.07289630  0.086838906
[18,] 20bis_jeanpierre_et_jacques-y2              0.74527211  0.75761489 -0.012342784
[19,] 27_la_bonne__cuisine_avec_des_navets-v1     0.55620902  0.46191082  0.094298195
[20,] 27_la_bonne__cuisine_avec_des_navets-v2     0.75734898  0.78706477 -0.029715785
[21,] 27_la_bonne__cuisine_avec_des_navets-v3     0.63477632  0.67399222 -0.039215902
[22,] 27bis_la_bonne_cuisine__avec_des_navets-m1  0.79839265  0.81409403 -0.015701381
[23,] 27bis_la_bonne_cuisine__avec_des_navets-m2  0.79309800  0.81268789 -0.019589886
[24,] 27bis_la_bonne_cuisine__avec_des_navets-m3  0.71406581  0.73534539 -0.021279577
[25,] 2bis_tu_paraitrais_soucieux-a_g             0.67999388  0.69325753 -0.013263648
[26,] 2bis_tu_paraitrais_soucieux-b_y             0.55944107  0.39934000  0.160101072

There doesn’t seem to be a huge amount of difference when intensity is taken into account. I’m not sure that the weighted correlation is more accurate.

mean(gest_scores$corr_diff)
[1] -0.0345533
max(gest_scores$corr_diff)
[1] 0.1601011
min(gest_scores$corr_diff)
[1] -0.2278366
sd(gest_scores$corr_diff)
[1] 0.08755922

Just for completeness, let’s make some plots…

plot_gest_and_ref <- function(gest_name) {
  # Get the index of the gesture
  index<-get_gest_index(gest_name)
  # Use it to get the freq of the gesture
  gest_pitch <- gest_interps[[index]]$f
  
  # Get the reference phrase
  phrase_name <- get_ref_name(gest_name)    
  # Use it to get the prosogram pitch ref and the intensity
  prosogram_pitch <- get_ref_ptsf(phrase_name)
  intensity <- get_ref_tsi(phrase_name)
  
  row <- filter(gest_scores, gest==gest_name)
  wcorr <- round(row$wcorr, 4)
  corr <- round(row$corr, 4)
  
  my_data <- tibble(gest=gest_pitch, ref=prosogram_pitch, i=intensity, 
                    percent=gest_interps[[index]]$index)
  
  my_plot <- ggplot(data=my_data, aes(x=percent, y=gest, color="gesture")) +
    scale_color_discrete(name = "Data source") +
    geom_line() +
    geom_line(data=my_data, aes(x=percent, y=ref, color="reference")) +
    geom_line(data=my_data, aes(x=percent, y=i, color="intensity")) +
    labs(title=paste(gest_name, "with its reference"), 
            subtitle=paste("Weighted Correlation:", wcorr, "Non-weighted:", corr),
            y="frequency (semitones from 130hz) \nunspecified for intensity",
            x="percent time")
  return(my_plot)
}

5 Comparison of scrub

5.1 Correlation

Scrub should go as linearly as possible, so we can compare it with the index (percent time). First look at correlation.

print(as.matrix(gest_scores_timing), quote = FALSE)
      gest                                       corr_timing
 [1,] 1_Il_nait_tres_premature-a_c               0.9905220  
 [2,] 1_Il_nait_tres_premature-b_m               0.9926963  
 [3,] 10_jean_saigne_beaucoup-a_m                0.9975551  
 [4,] 10bis_jenseigne_beaucoup-l1                0.9919548  
 [5,] 10bis_jenseigne_beaucoup-l2                0.9963043  
 [6,] 13_la_belle_ferme_le_voile-c1              0.9806312  
 [7,] 13_la_belle_ferme_le_voile-c2              0.9961320  
 [8,] 13bis_la_belle_ferme__le_voile-g1          0.9948027  
 [9,] 13bis_la_belle_ferme__le_voile-g2          0.9938317  
[10,] 1bis_Il_naitrait_premature-a_y             0.9946051  
[11,] 1bis_Il_naitrait_premature-b_g             0.9924693  
[12,] 2_tu_parais_tres_soucieux-a_c              0.9812790  
[13,] 2_tu_parais_tres_soucieux-b_s              0.9917875  
[14,] 20_jean_pierre_et_jacques-s1               0.9928272  
[15,] 20_jean_pierre_et_jacques-s2               0.9910181  
[16,] 20_jean_pierre_et_jacques-s3               0.9902815  
[17,] 20bis_jeanpierre_et_jacques-y1             0.9696587  
[18,] 20bis_jeanpierre_et_jacques-y2             0.9847867  
[19,] 27_la_bonne__cuisine_avec_des_navets-v1    0.9951787  
[20,] 27_la_bonne__cuisine_avec_des_navets-v2    0.9880714  
[21,] 27_la_bonne__cuisine_avec_des_navets-v3    0.9910955  
[22,] 27bis_la_bonne_cuisine__avec_des_navets-m1 0.9954252  
[23,] 27bis_la_bonne_cuisine__avec_des_navets-m2 0.9382105  
[24,] 27bis_la_bonne_cuisine__avec_des_navets-m3 0.9946982  
[25,] 2bis_tu_paraitrais_soucieux-a_g            0.9861794  
[26,] 2bis_tu_paraitrais_soucieux-b_y            0.9879635  

They are all very high, but within that range we can still check out the gestes with lower score. 23 and 17 are the only ones that are lower than .98.

** 23 ** Gesture Original


** 17 ** Gesture Original

These definitely have more problematic rhythms. But it’s really hard to draw the line on what is good and what is bad.

5.2 Distance

Let’s try another measurement. Correlation and RMSE subtracts the mean, but the mean doesn’t have much sense when we talk about timing. So let’s just sum up all the distance between all the points. A higher number means less accurate timing.

One issue is that longer phrases have more points. This is problematic because longer phrases will generally have a larger total distance. We can try to mitigate that by taking the average, but that’s also problematic because a longer phrase with inaccurate timing in one place will not necessarily show.

So we have to resample the gestures such that they all have the same number of points.

print(as.matrix(gest_scores_timing2), quote=FALSE)
      gest                                       corr_timing dist_timing
 [1,] 1_Il_nait_tres_premature-a_c               0.9905220   1.2309261  
 [2,] 1_Il_nait_tres_premature-b_m               0.9926963   0.9910077  
 [3,] 10_jean_saigne_beaucoup-a_m                0.9975551   0.4683937  
 [4,] 10bis_jenseigne_beaucoup-l1                0.9919548   1.3140657  
 [5,] 10bis_jenseigne_beaucoup-l2                0.9963043   1.4735137  
 [6,] 13_la_belle_ferme_le_voile-c1              0.9806312   0.9829001  
 [7,] 13_la_belle_ferme_le_voile-c2              0.9961320   0.6755210  
 [8,] 13bis_la_belle_ferme__le_voile-g1          0.9948027   1.0423035  
 [9,] 13bis_la_belle_ferme__le_voile-g2          0.9938317   0.7180360  
[10,] 1bis_Il_naitrait_premature-a_y             0.9946051   1.0962586  
[11,] 1bis_Il_naitrait_premature-b_g             0.9924693   1.3090820  
[12,] 2_tu_parais_tres_soucieux-a_c              0.9812790   1.0466100  
[13,] 2_tu_parais_tres_soucieux-b_s              0.9917875   0.8721954  
[14,] 20_jean_pierre_et_jacques-s1               0.9928272   1.0588987  
[15,] 20_jean_pierre_et_jacques-s2               0.9910181   1.0336173  
[16,] 20_jean_pierre_et_jacques-s3               0.9902815   0.8978326  
[17,] 20bis_jeanpierre_et_jacques-y1             0.9696587   1.7163142  
[18,] 20bis_jeanpierre_et_jacques-y2             0.9847867   1.2453728  
[19,] 27_la_bonne__cuisine_avec_des_navets-v1    0.9951787   0.9388242  
[20,] 27_la_bonne__cuisine_avec_des_navets-v2    0.9880714   0.6817296  
[21,] 27_la_bonne__cuisine_avec_des_navets-v3    0.9910955   1.1013136  
[22,] 27bis_la_bonne_cuisine__avec_des_navets-m1 0.9954252   0.2810471  
[23,] 27bis_la_bonne_cuisine__avec_des_navets-m2 0.9382105   1.0535572  
[24,] 27bis_la_bonne_cuisine__avec_des_navets-m3 0.9946982   0.8490400  
[25,] 2bis_tu_paraitrais_soucieux-a_g            0.9861794   1.4341401  
[26,] 2bis_tu_paraitrais_soucieux-b_y            0.9879635   1.2716504  
plot_gest_and_ref_timing <- function(gest_name) {
  # Get the index of the gesture
  index<-get_gest_index(gest_name)
  # Use it to get the freq of the gesture
  gest_scrub <- gest_interps[[index]]$scrub
  gest_index <- gest_interps[[index]]$index

  row <- filter(gest_scores_timing2, gest==gest_name)
  corr <- round(row$corr_timing, 4)
  dist <- round(row$dist_timing, 4)
  
  my_data <- tibble(gest=gest_scrub, ref=gest_index)
  
  my_plot <- ggplot(data=my_data, aes(x=ref, y=gest, color="gesture")) +
    scale_color_discrete(name = "Data source") +
    geom_line() +
    geom_line(data=my_data, aes(x=ref, y=ref, color="ideal")) +
    labs(title=paste("Timing of", gest_name, "compared with ideal linear time"), 
            subtitle=paste("Correlation:", corr, "Distance:", dist),
            y="Scrub percentage",
            x="percent time")
  return(my_plot)
}
Warning message:
replacing previous import ‘vctrs::data_frame’ by ‘tibble::data_frame’ when loading ‘dplyr’ 

5.3 Best timing scores (<0.5)

Gesture Original

This is actually quite good. The pitch curve can be closer to the original, but it has the right overall contour.


plot_gest_and_ref_timing(gests[[3]])

plot_gest_and_ref(gests[[3]])

Gesture Original

Even though the timing follows very closely the linear idea, this does not sound right because the frequency curve is incorrect.


5.4 Quite good scores (<0.1)

plot_gest_and_ref_timing(gests[[7]])

plot_gest_and_ref(gests[[7]])

Gesture Original

This is a very good gesture. The timing is not exactly linear, but it’s close enough.


plot_gest_and_ref_timing(gests[[9]])

plot_gest_and_ref(gests[[9]])

Gesture Original

This is also a very good gesture for both frequency and timing. Timing is not perfect, once again, but it’s good enough.


plot_gest_and_ref_timing(gests[[20]])

plot_gest_and_ref(gests[[20]])

Gesture Original

This sounds a bit exaggerated, but it’s still definitely correct.


5.5 Worst timing scores (>1)

plot_gest_and_ref_timing(gests[[17]])

plot_gest_and_ref(gests[[17]])

Gesture Original

This gesture does not score well for either pitch or timing.


plot_gest_and_ref_timing(gests[[11]])

plot_gest_and_ref(gests[[11]])

Gesture Original

While this one has a very good score for pitch, taking the timing score into account gives a score that more accurately reflect the strangeness that I hear.


plot_gest_and_ref_timing(gests[[25]])

plot_gest_and_ref(gests[[25]])

Gesture Original

This has an ok score for pitch, but the timing score reflects how weirdly it sounds.


LS0tCnRpdGxlOiAiQ29tcGFyZSBHZXN0dXJlcyBQYXJ0IElJOiBJbnRlbnNpdHkgd2VpZ2h0aW5nLCB0aW1pbmcgZGlzdGFuY2UiCmF1dGhvcjogIlhpYW8gWGlhbyIKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIsICVZJylgIiAKb3V0cHV0OiAKIAogaHRtbF9kb2N1bWVudDoKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICB0b2M6IHllcwogaHRtbF9ub3RlYm9vazoKICAgIGRlcHRoOiA0CiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRoZW1lOiB1bml0ZWQKICAgIHRvYzogeWVzCi0tLQoKVGhlIGdvYWwgb2YgdGhpcyBub3RlYm9vayBpcyB0byBmaWd1cmUgb3V0IHRoZSBiZXN0IHdheSB0byBjb21wYXJlIHN1YmplY3RzJyBnZXN0dXJlcyB3aXRoIHRoZWlyIHJlZmVyZW5jZXMuIFNvbWUgcXVlc3Rpb25zIG9mIGludGVyZXN0OgoKKiBDb21wYXJlIHdpdGggb3JpZ2luYWwgcGl0Y2ggY3VydmUgb3IgUHJvc29ncmFtIHN0eWxpemF0aW9uPwoqIEhvdyBtdWNoIGRpZmZlcmVuY2UgZG9lcyBpdCBtYWtlIHdoZW4gdGFraW5nIGludG8gYWNjb3VudCBvZiBpbnRlbnNpdHk/CiogV2hhdCBoYXBwZW5zIHdoZW4gd2UgYWxzbyBjb21wYXJlIHNjcnViPwoKIyBMb2FkaW5nIFBpdGNoVGllciAmIEludGVuc2l0eVRpZXIgZmlsZXMKClVzaW5nIHJQcmFhdCwgbG9hZCBhIHRoZSBQcmFhdCBQaXRjaFRpZXIsIFByb3NvZ3JhbSBQaXRjaFRpZXIsIGFuZCBJbnRlbnNpdHlUaWVyIG9mIGFuIGV4YW1wbGUgZmlsZSwgaW50ZXJwb2xhdGUgdG8gZ2V0IHRoZSBmMCBhbmQgaW50ZW5zaXR5IHZhbHVlcyBldmVyeSAxMG1zIGFuZCBwbG90LiBDb21wYXJpbmcgYXQgMTAgbXMgaW50ZXJ2YWxzIGNvbWVzIGZyb20gZCdBbGVzc2FuZHJvIGV0IGFsIDIwMTEuCgpJbiA2X2NvbXBhcmVHZXN0dXJlLCBJIHVzZWQgdGhlIEpTT04gdmVyc2lvbnMgb2YgdGhlIFByb3NvZ3JhbSBwaXRjaCB0aWVycywgYnV0IGl0J3MgYWN0dWFsbHkgYmV0dGVyIHRvIGRpcmVjdGx5IHVzZSB0aGUgX3N0eWwuUGl0Y2hUaWVyIGZpbGVzIGJlY2F1c2UgclByYWF0IGNhbiBkbyBpbnRlcnBvbGF0aW9uIGF1dG9tYXRpY2FsbHkuCgpgYGB7ciwgZmlnLmtlZXA9J2FsbCd9CmxpYnJhcnkoInpvbyIpCmxpYnJhcnkoInJQcmFhdCIpCmxpYnJhcnkoInRpZHl2ZXJzZSIpCgpwYXRoPSIuLi9kYXRhLzIwXzEyXzE1LXBpbG90X2dlc3R1cmVzL2FtYmlndW91cy9yZWZlcmVuY2UvIgoKIyBGaW5kIGludGVuc2l0eSBmaWxlcwpzZXR3ZChwYXN0ZTAocGF0aCwgImludGVuc2l0eSIpKQppdF9maWxlbmFtZXMgPC0gZGlyKHBhdHRlcm49IlxcLkludGVuc2l0eVRpZXIkIikKCiMgRmluZCBwaXRjaFRpZXIgZmlsZXMgCnNldHdkKCIuLi9waXRjaHRpZXIiKQpwdF9maWxlbmFtZXMgPC0gZGlyKHBhdHRlcm49IlxcLlBpdGNoVGllciQiKQoKIyBGaW5kIFByb3NvZ3JhbSBQaXRjaFRpZXIgZmlsZXMKc2V0d2QoIi4uL3Byb3NvZ3JhbV9wdCIpCnBzZ19maWxlbmFtZXMgPC0gZGlyKHBhdHRlcm49IlxcLlBpdGNoVGllciQiKQoKIyBJbnRlcnBvbGF0ZSBwaXRjaCBhbmQgaW50ZW5zaXR5IGZvciBvbmUgcGhyYXNlIGFuZCBwbG90CnNldHdkKCIuLi8uLi8uLi8uLi8uLi9ub3RlYm9va3MvIikKCml0MSA8LSBpdC5yZWFkKHBhc3RlMChwYXRoLCAiaW50ZW5zaXR5LyIsIGl0X2ZpbGVuYW1lc1sxXSkpCml0MV9pbnRlcnAgPC0gaXQuaW50ZXJwb2xhdGUoaXQxLCBzZXEoMCwgaXQxJHRtYXgsIGJ5PTAuMDEpKQoKcHQxIDwtIHB0LnJlYWQocGFzdGUwKHBhdGgsICJwaXRjaHRpZXIvIiwgcHRfZmlsZW5hbWVzWzFdKSkKcHQxX2ludGVycCA8LSBwdC5pbnRlcnBvbGF0ZShwdDEsIHNlcSgwLCBwdDEkdG1heCwgYnk9MC4wMSkpCgpwc2cxIDwtIHB0LnJlYWQocGFzdGUwKHBhdGgsICJwcm9zb2dyYW1fcHQvIiwgcHNnX2ZpbGVuYW1lc1sxXSkpCnBzZzFfaW50ZXJwIDwtIHB0LmludGVycG9sYXRlKHBzZzEsIHNlcSgwLCBwc2cxJHRtYXgsIGJ5PTAuMDEpKQoKcmVmX2RhdGEgPC0gdGliYmxlKHQ9cHQxX2ludGVycCR0LCBmPXB0MV9pbnRlcnAkZiwgcHNnLmY9cHNnMV9pbnRlcnAkZiwgaT1pdDFfaW50ZXJwJGkpCgojIFBsb3QKbmFtZSA8LSBzdHJfc3BsaXQoaXRfZmlsZW5hbWVzWzFdLCAnLkludGVuc2l0eVRpZXInKVtbMV1dWzFdCmdncGxvdChkYXRhPXJlZl9kYXRhLCBhZXMoeD10LCB5PWYpKSArCiAgICAgIGdlb21fbGluZShhZXMoY29sb3I9IlByYWF0IFBpdGNoIikpICsKICAgICAgc2NhbGVfY29sb3JfZGlzY3JldGUobmFtZSA9ICJEYXRhIikgICsKICAgICAgZ2VvbV9saW5lKGFlcyh4PXQsIHk9aSwgY29sb3I9IkludGVuc2l0eSIpKSArCiAgICAgIGdlb21fbGluZShhZXMoeD10LCB5PXBzZy5mLCBjb2xvcj0iUHJvc29ncmFtIFBpdGNoIikpICsKICAgICAgbGFicyh0aXRsZT0iSW50ZXJwb2xhdGVkIHBpdGNoIHRpZXIgYW5kIGludGVuc2l0eSB0aWVycyIsIHN1YnRpdGxlPXBhc3RlKCJQaHJhc2U6IixuYW1lKSwKICAgICAgICAgIHg9InRpbWUgKHNlY29uZHMpIiwKICAgICAgICAgIHk9IkZyZXF1ZW5jeSAoSHopIGZvciBwaXRjaCwgXG51bnNwZWNpZmllZCBmb3IgaW50ZW5zaXR5IikKCmBgYAoKSW50ZXJlc3RpbmdseSwgdGhlIFByYWF0IFBpdGNoIGN1cnZlIGFuZCB0aGUgUHJvc29ncmFtIG9uZSBmb3IgdGhpcyBwYXJ0aWN1bGFyIHBocmFzZSBkaWZmZXIgYnkgd2F5IG1vcmUgdGhhbiBJIGhhZCBleHBlY3RlZC4gVGhlIHBpdGNoIGdvZXMgd2F5IHRvbyBoaWdoIGluIFByYWF0J3MgYW5hbHlzaXMuIEkgdGhpbmsgdGhvc2UgYXJlIGFydGlmYWN0cy4KCiMgQ29tcGFyZSBvcmlnaW5hbCBwaXRjaCBjdXJ2ZSB3aXRoIFByb3NvZ3JhbSBzdHlsaXphdGlvbgoKKipTdW1tYXJ5OioqIFVzZSBQcm9zb2dyYW0gc3R5bGl6YXRpb24uIFByYWF0J3MgcGl0Y2ggYW5hbHlzaXMgaGFzIHNvbWUgYXJ0aWZhY3RzIGFuZCB3ZWlyZCBtaWNyb3Byb3NvZHkuIFdlaWdoaW5nIGJ5IGludGVuc2l0eSBkb2Vzbid0IHNlZW0gdG8gbWFrZSBhIGh1Z2UgZGlmZmVyZW5jZSwgYnV0IGl0IGRvZXNuJ3QgdGFrZSBzbyBtdWNoIG1vcmUgd29yay4gQWxzbywgc2luY2UgZCdBbGVzc2FuZHJvIGV0IGFsIDIwMTEgdXNlZCBpdCwgaXQncyBnb29kIHRvIGJlIGNvbnNpc3RlbnQuCgoqKlByb2Nlc3M6KiogCk5vdyBsZXQncyBjb21wYXJlIHRoZSBQcmFhdCBQaXRjaCBjdXJ2ZSBhbmQgUHJvc29ncmFtIHBpdGNoIGN1cnZlIGZvciBhbGwgdGhlIGZpbGVzLgoKSW4gNl9jb21wYXJlR2VzdHVyZSwgSSBhcmJpdHJhcmlseSBwaWNrZWQgMTAwIGFzIHRoZSBudW1iZXIgb2YgZGF0YSBwb2ludHMgdG8gY29tcGFyZS4gTm93IHdlIGNhbiBjb21wYXJlIGV2ZXJ5IDEwbXMuIFRoaXMgbWVhbnMgdGhhdCB0aGUgY3VydmVzIGZvciBlYWNoIGZpbGUgaGFzIGEgZGlmZmVyZW50IG51bWJlciBvZiBwb2ludHMuCgpGaXJzdCwgZ2V0IHRoZSBpbnRlcnBvbGF0ZWQgdGltZSBzZXJpZXMgdmFsdWVzIGZvciBhbGwgdGhlIG9yaWdpbmFsIHBocmFzZXMuIFRoZSBwaXRjaCB2YWx1ZXMgYXJlIGNvbnZlcnRlZCB0byBzZW1pdG9uZXMgd2l0aCBhIHJlZmVyZW5jZSBvZiAxMzBIeiB0byBtYXRjaCB0aGUgaW5wdXQgZ2VzdHVyZXMuCgoKYGBge3J9CnNvdXJjZSgiLi4vdXRpbHMvaGVydHpUb1NULlIiKQoKIyBBIHZlY3RvciB3aXRoIGFsbCB0aGUgcGhyYXNlcwpyZWZfcGhyYXNlcyA8LSBkby5jYWxsKCJyYmluZCIsIGxhcHBseShwc2dfZmlsZW5hbWVzLCBmdW5jdGlvbihlbHQpIHsKICBzdHJfc3BsaXQoZWx0LCAnLmpzb24nKVtbMV1dWzFdCn0pKVssMV0KCiMgUHV0IGFsbCB0aGUgcmVmZXJlbmNlIHBpdGNoIHRpZXJzIGludG8gYSBsaXN0CnJlZl9wdHNfaW50ZXJwIDwtIGxhcHBseShwdF9maWxlbmFtZXMsIGZ1bmN0aW9uKGVsdCkgewogIG15X3B0PC1wdC5yZWFkKHBhc3RlMChwYXRoLCAicGl0Y2h0aWVyLyIsIGVsdCkpCiAgaW50ZXJwb2xhdGVkIDwtIHB0LmludGVycG9sYXRlKG15X3B0LCBzZXEoMCwgbXlfcHQkdG1heCwgYnk9MC4wMSkpCiAgaW50ZXJwb2xhdGVkJGYgPC0gaGVydHpfdG9fc2VtaXRvbmVzKGludGVycG9sYXRlZCRmLCAxMzApCiAgaW50ZXJwb2xhdGVkCn0pCgojIFB1dCBhbGwgdGhlIFByb3NvZ3JhbSBwaXRjaCB0aWVycyBpbnRvIGEgbGlzdApyZWZfcHNnc19pbnRlcnAgPC0gbGFwcGx5KHBzZ19maWxlbmFtZXMsIGZ1bmN0aW9uKGVsdCkgewogIG15X3B0PC1wdC5yZWFkKHBhc3RlMChwYXRoLCAicHJvc29ncmFtX3B0LyIsIGVsdCkpCiAgaW50ZXJwb2xhdGVkIDwtIHB0LmludGVycG9sYXRlKG15X3B0LCBzZXEoMCwgbXlfcHQkdG1heCwgYnk9MC4wMSkpCiAgaW50ZXJwb2xhdGVkJGYgPC0gaGVydHpfdG9fc2VtaXRvbmVzKGludGVycG9sYXRlZCRmLCAxMzApCiAgaW50ZXJwb2xhdGVkCn0pCgojIFB1dCBhbGwgdGhlIHJlZmVyZW5jZSBpbnRlbnNpdHkgdGllcnMgaW50byBhIGxpc3QKcmVmX2l0c19pbnRlcnAgPC0gbGFwcGx5KGl0X2ZpbGVuYW1lcywgZnVuY3Rpb24oZWx0KSB7CiAgbXlfaXQgPC0gaXQucmVhZChwYXN0ZTAocGF0aCwgImludGVuc2l0eS8iLCBlbHQpKQogIGl0LmludGVycG9sYXRlKG15X2l0LCBzZXEoMCwgbXlfaXQkdG1heCwgYnk9MC4wMSkpCn0pCgojIEdldCB0aGUgdGltZXMgb2YgZWFjaCBwaHJhc2UKcmVmX3RpbWVzIDwtIGRvLmNhbGwoInJiaW5kIiwgbGFwcGx5KHJlZl9wdHNfaW50ZXJwLCBmdW5jdGlvbihlbHQpIHsKICBlbHQkdG1heAp9KSlbLDFdCgojIEEgdGliYmxlIGNvbnRhaW5pbmcgZWFjaCBwaHJhc2UgYW5kIGl0cyBkdXJhdGlvbgpyZWZfaW5mbyA8LSB0aWJibGUocGhyYXNlPXJlZl9waHJhc2VzLCBkdXJhdGlvbj1yZWZfdGltZXMpCgojIEEgZnVuY3Rpb24gdGhhdCBnZXRzIHRoZSBydWF0aW9uIGdpdmVuIHRoZSBwaHJhc2UgbmFtZQpnZXRfcmVmX2R1cmF0aW9uIDwtIGZ1bmN0aW9uKHBocmFzZV9uYW1lKSB7CiAgaW5kZXg8LW1hdGNoKHBocmFzZV9uYW1lLCByZWZfaW5mbyRwaHJhc2UpCiAgaWYgKGlzLm5hKGluZGV4KSkgeyByZXR1cm4oTlVMTCkgfQogIHJldHVybihyZWZfaW5mbyRkdXJhdGlvbltpbmRleF0pCn0KCiMgR2V0IHRoZSB0aW1lcyBzdGFtcHMgZm9yIGludGVycG9sYXRpb24gZ2l2ZW4gdGhlIHBocmFzZSBuYW1lCmdldF9yZWZfdGltZXN0YW1wcyA8LSBmdW5jdGlvbihwaHJhc2VfbmFtZSkgewogIGluZGV4PC1tYXRjaChwaHJhc2VfbmFtZSwgcmVmX2luZm8kcGhyYXNlKQogIGlmIChpcy5uYShpbmRleCkpIHsgcmV0dXJuKE5VTEwpIH0KICByZXR1cm4ocmVmX3B0c19pbnRlcnBbW2luZGV4XV0kdCkKfQoKIyBHZXQgdGhlIGludGVycG9sYXRlZCBmcmVxdWVuY3kgdmFsdWVzIGdpdmVuIHRoZSBwaHJhc2UgbmFtZQojIChmcmVxdWVuY3kgdGltZSBzZXJpZXMpCmdldF9yZWZfdHNmIDwtIGZ1bmN0aW9uKHBocmFzZV9uYW1lKSB7CiAgaW5kZXg8LW1hdGNoKHBocmFzZV9uYW1lLCByZWZfaW5mbyRwaHJhc2UpCiAgaWYgKGlzLm5hKGluZGV4KSkgeyByZXR1cm4oTlVMTCkgfQogIHJldHVybihyZWZfcHRzX2ludGVycFtbaW5kZXhdXSRmKQp9CgojIEdldCB0aGUgaW50ZXJwb2xhdGVkIHByb3NvZ3JhbSBmcmVxdWVuY3kgdmFsdWVzIGdpdmVuIHRoZSBwaHJhc2UgbmFtZQojIChwcm9zb2dyYW0gZnJlcXVlbmN5IHRpbWUgc2VyaWVzKQpnZXRfcmVmX3B0c2YgPC0gZnVuY3Rpb24ocGhyYXNlX25hbWUpIHsKICBpbmRleDwtbWF0Y2gocGhyYXNlX25hbWUsIHJlZl9pbmZvJHBocmFzZSkKICBpZiAoaXMubmEoaW5kZXgpKSB7IHJldHVybihOVUxMKSB9CiAgcmV0dXJuKHJlZl9wc2dzX2ludGVycFtbaW5kZXhdXSRmKQp9CgojIEdldCB0aGUgaW50ZXJwb2xhdGVkIGludGVuc2l0eSB2YWx1ZXMgZ2l2ZW4gdGhlIHBocmFzZSBuYW1lCiMgKGludGVuc2l0eSB0aW1lIHNlcmllcykKZ2V0X3JlZl90c2kgPC1mdW5jdGlvbihwaHJhc2VfbmFtZSkgewogIGluZGV4PC1tYXRjaChwaHJhc2VfbmFtZSwgcmVmX2luZm8kcGhyYXNlKQogIGlmIChpcy5uYShpbmRleCkpIHsgcmV0dXJuKE5VTEwpIH0KICByZXR1cm4ocmVmX2l0c19pbnRlcnBbW2luZGV4XV0kaSkKfQpgYGAKCk5vdyBsZXQncyBjb21wYXJlIHRoZSBQcmFhdCBQaXRjaCBjdXJ2ZSB3aXRoIHRoZSBQcm9zb2dyYW0gcGl0Y2ggY3VydmUgZm9yIGVhY2ggcGhyYXNlOgoKYGBge3J9CiMgSGVscGVyIGZ1bmN0aW9uIHRvIGZpbmQgdGhlIHN0cmFpZ2h0IHVwIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIHByYWF0IGFuZCBwcm9zb2dyYW0gZnJlcXVlbmN5IGN1cnZlcwpnZXRfY29ycl9wcmFhdF9wc2cgPC0gZnVuY3Rpb24oZ2VzdF9uYW1lKSB7CiAgcHJhYXRfcGl0Y2ggPC0gZ2V0X3JlZl90c2YoZ2VzdF9uYW1lKQogIHByb3NvZ3JhbV9waXRjaCA8LSBnZXRfcmVmX3B0c2YoZ2VzdF9uYW1lKQogIHJldHVybihjb3IocHJhYXRfcGl0Y2gsIHByb3NvZ3JhbV9waXRjaCwgbWV0aG9kPSJwZWFyc29uIikpCn0KCmNvcnJfcHJhYXRfcHNnX3JlZnMgPC0gZG8uY2FsbCgicmJpbmQiLCBsYXBwbHkocmVmX3BocmFzZXMsIGZ1bmN0aW9uKGVsdCkgewogIGdldF9jb3JyX3ByYWF0X3BzZyhlbHQpCn0pKVssMV0KCnJlc3VsdHNfY29ycl9wcmFhdF9wcm9zb2dyYW0gPC0gdGliYmxlKHBocmFzZT1yZWZfcGhyYXNlcywgY29yci5wcmFhdC5wc2c9Y29ycl9wcmFhdF9wc2dfcmVmcykKcHJpbnQoYXMubWF0cml4KHJlc3VsdHNfY29ycl9wcmFhdF9wcm9zb2dyYW0pLCBxdW90ZSA9IEZBTFNFKQoKYGBgCmBgYHtyfQptZWFuKHJlc3VsdHNfY29ycl9wcmFhdF9wcm9zb2dyYW0kY29yci5wcmFhdC5wc2cpCmBgYAoKRG9lcyBpdCBtYWtlIGEgZGlmZmVyZW5jZSBpZiB3ZSB3ZWlnaCBieSBpbnRlbnNpdHk/CmBgYHtyfQpsaWJyYXJ5KCJ3Q29yciIpICMgZm9yIHRoZSB3ZWlnaHRlZCBjb3JyZWxhdGlvbiBmdW5jdGlvbgoKCmdldF93ZWlnaHRlZF9jb3JyX3ByYWF0X3BzZyA8LSBmdW5jdGlvbihwaHJhc2VfbmFtZSkgewogIHByYWF0X3BpdGNoIDwtIGdldF9yZWZfdHNmKHBocmFzZV9uYW1lKQogIHByb3NvZ3JhbV9waXRjaCA8LSBnZXRfcmVmX3B0c2YocGhyYXNlX25hbWUpCiAgaW50ZW5zaXR5IDwtIGdldF9yZWZfdHNpKHBocmFzZV9uYW1lKQogIAogIHdjIDwtIHdlaWdodGVkQ29ycihwcmFhdF9waXRjaCwgcHJvc29ncmFtX3BpdGNoLCBtZXRob2QgPSAiUGVhcnNvbiIsIAogICAgICAgICAgICAgICB3ZWlnaHRzID0gaW50ZW5zaXR5LCBNTCA9IEZBTFNFLCBmYXN0ID0gVFJVRSkKICByZXR1cm4od2MpCn0KCndjb3JyX3ByYWF0X3BzZ19yZWZzIDwtIGRvLmNhbGwoInJiaW5kIiwgbGFwcGx5KHJlZl9waHJhc2VzLCBmdW5jdGlvbihlbHQpIHsKICBnZXRfd2VpZ2h0ZWRfY29ycl9wcmFhdF9wc2coZWx0KQp9KSlbLDFdCgpyZXN1bHRzX3djb3JyX3ByYWF0X3Byb3NvZ3JhbSA8LSB0aWJibGUocGhyYXNlPXJlZl9waHJhc2VzLCBjb3JyLnByYWF0LnBzZz1jb3JyX3ByYWF0X3BzZ19yZWZzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2NvcnIucHJhYXQucHNnPXdjb3JyX3ByYWF0X3BzZ19yZWZzKQpwcmludChhcy5tYXRyaXgocmVzdWx0c193Y29ycl9wcmFhdF9wcm9zb2dyYW0pLCBxdW90ZSA9IEZBTFNFKQoKYGBgCgpgYGB7cn0KbWVhbihyZXN1bHRzX3djb3JyX3ByYWF0X3Byb3NvZ3JhbSR3Y29yci5wcmFhdC5wc2cpCmBgYAoKVGhlIG1lYW4gb2YgdGhlIHdlaWdodGVkIGNvcnJlbGF0aW9uIGlzIGEgbGl0dGxlIGhpZ2hlciBidXQgbm90IGJ5IG11Y2guCgojIFBsb3R0aW5nIHNvbWUgbW9yZSBleGFtcGxlcwoKYGBge3J9CgpwbG90X3JlZl9jdXJ2ZXMgPC0gZnVuY3Rpb24ocGhyYXNlX25hbWUpIHsKICAjIGZpcnN0IGdldCB0aGUgaW5mbwogIHByYWF0X3BpdGNoIDwtIGdldF9yZWZfdHNmKHBocmFzZV9uYW1lKQogIHByb3NvZ3JhbV9waXRjaCA8LSBnZXRfcmVmX3B0c2YocGhyYXNlX25hbWUpCiAgaW50ZW5zaXR5IDwtIGdldF9yZWZfdHNpKHBocmFzZV9uYW1lKQogIHRpbWVzdGFtcHMgPC0gZ2V0X3JlZl90aW1lc3RhbXBzKHBocmFzZV9uYW1lKQogIAogICMgY29tYmluZSBpbnRvIGEgdGliYmxlCiAgcmVmX2RhdGEgPC0gdGliYmxlKGY9cHJhYXRfcGl0Y2gsIGk9aW50ZW5zaXR5LCBwc2cuZj1wcm9zb2dyYW1fcGl0Y2gsIHQ9dGltZXN0YW1wcykKICAKICAjIHBsb3QKICBnZ3Bsb3QoZGF0YT1yZWZfZGF0YSwgYWVzKHg9dCwgeT1mKSkgKwogICAgICBnZW9tX2xpbmUoYWVzKGNvbG9yPSJQcmFhdCBQaXRjaCIpKSArCiAgICAgIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKG5hbWUgPSAiRGF0YSIpICArCiAgICAgIGdlb21fbGluZShhZXMoeD10LCB5PWksIGNvbG9yPSJJbnRlbnNpdHkiKSkgKwogICAgICBnZW9tX2xpbmUoYWVzKHg9dCwgeT1wc2cuZiwgY29sb3I9IlByb3NvZ3JhbSBQaXRjaCIpKSArCiAgICAgIGxhYnModGl0bGU9IkludGVycG9sYXRlZCBwaXRjaCB0aWVyIGFuZCBpbnRlbnNpdHkgdGllcnMiLCBzdWJ0aXRsZT1wYXN0ZSgiUGhyYXNlOiIscGhyYXNlX25hbWUpLAogICAgICAgICAgeD0idGltZSAoc2Vjb25kcykiLAogICAgICAgICAgeT0iRnJlcXVlbmN5IChIeikgZm9yIHBpdGNoLCBcbnVuc3BlY2lmaWVkIGZvciBpbnRlbnNpdHkiKSAgCn0KCmBgYAoKVGhlIG1vc3QgZGlmZmVyZW50LiBIZXJlIEkgdGhpbmsgUHJvc29ncmFtIGlzIG1vcmUgYWNjdXJhdGUuIFRoZSBwYXJ0IHRoYXQgZ29lcyByZWFsbHkgbG93IGFuZCByZWFsbHkgaGlnaCBpbiBQcmFhdCdzIHBpdGNoIGN1cnZlIHNlZW0gdG8gYmUgYXJ0aWZhY3RzLgoKYGBge3IsIGZpZy5rZWVwPSdhbGwnfQpwbG90X3JlZl9jdXJ2ZXMocmVmX3BocmFzZXNbNl0pCmBgYAoKVGhlIG1vc3Qgc2ltaWxhcgoKYGBge3J9CnBsb3RfcmVmX2N1cnZlcyhyZWZfcGhyYXNlc1sxMF0pCmBgYAoKIyBXZWlnaHRlZCBjb21wYXJpc29uIG9mIFByb3NvZ3JhbSBwaXRjaCBjdXJ2ZSB3aXRoIGxlYXJuZXJzJyBnZXN0dXJlcwoKTG9hZCBnZXN0dXJlcy4gSW50ZXJwb2xhdGUgYmFzZWQgb24gdGhlIG51bWJlciBvZiBwb2ludHMgaW4gdGhlIHJlZmVyZW5jZSBnZXN0dXJlLiBUaGUgaW5kZXggaW5kaWNhdGVzIHRpbWUgaW4gdGVybXMgb2YgYSBwZXJjZW50YWdlIGZyb20gMCB0byAxLgoKYGBge3J9CmxpYnJhcnkoImpzb25saXRlIikKbGlicmFyeSgidGltZXRrIikKCiMgR2V0IGFsbCB0aGUgZmlsZW5hbWVzIG9mIHRoZSBnZXN0dXJlcwpwYXRoPSIuLi9kYXRhLzIwXzEyXzE1LXBpbG90X2dlc3R1cmVzL2FtYmlndW91cy9nZXN0dXJlcy8iCnNldHdkKHBhdGgpCmdlc3RfZmlsZW5hbWVzIDwtIGRpcihwYXR0ZXJuPSJcXC5nZXN0JCIpCm5hbWUgPC0gZ2VzdF9maWxlbmFtZXNbMV0KCiMgRXh0cmFjdCB0aGUgcmVmZXJlbmNlIHBocmFzZSBmcm9tIHRoZSB3aG9sZSBnZXN0dXJlIG5hbWUKZ2V0X3JlZl9uYW1lIDwtIGZ1bmN0aW9uKGdlc3RfbmFtZSkgewogIHJldHVybihzdHJfc3BsaXQoZ2VzdF9uYW1lLCAnLScpW1sxXV1bMV0pCn0KCiMgRmluZHMgdGhlIG51bWJlciBvZiBzYW1wbGVzIGluIHRoZSByZWZlcmVuY2UgZm9yIHRoZSBwYXJ0aWN1bGFyIGdlc3R1cmUKZ2V0X251bV9zYW1wbGVzIDwtZnVuY3Rpb24oZ2VzdF9uYW1lKSB7CiMgRmluZCBob3cgbWFueSBzYW1wbGVzIGFyZSBpbiB0aGUgcmVmIGZpbGUKICByZWZfcGhyYXNlIDwtIGdldF9yZWZfbmFtZShnZXN0X25hbWUpICAKICByZXR1cm4obGVuZ3RoKGdldF9yZWZfdGltZXN0YW1wcyhyZWZfcGhyYXNlKSkpCn0KCiMgTG9hZCBnZXN0IGRhdGEgYW5kIGNvbnZlcnQgdGltZQpwcmVwX2dlc3Q8LWZ1bmN0aW9uKGdlc3QpIHsKICAjIENvbnZlcnQgdGltZSB0byBwZXJjZW50YWdlCiAgbWF4X3RpbWUgPC0gdGFpbChnZXN0JHRfZW5kLCAxKQogIGdlc3QgPC0gZ2VzdCAlPiUgbXV0YXRlKHBlcmNlbnQ9dF9pbml0L21heF90aW1lKSAlPiUKICAgICAgICAgICAgICMgb25seSBrZWVwIGxvY2FsIHNjcnViIHRpbWUgYW5kIHRoZSBmcmVxIHZhbHVlcyAKICAgICAgICAgICAgIHNlbGVjdChwZXJjZW50LCBmLCBzY3J1YikKICByZXR1cm4oZ2VzdCkKfQoKIyBhcmdzOiBkYXRhIC0gdGliYmxlIHdpdGggY29sdW1uczogcGVyY2VudCAoJSBvZiB3YXkgdGhyb3VnaCBzaWduYWwpICYgZiAoZnJlcXVlbmN5IGF0IHRoYXQgcG9pbnQpLCAKIyBudW1fc2FtcGxlcyAtICMgZXF1YWxseSBzcGFjZWQgcG9pbnRzIGluIG91dHB0CiMgcmV0dXJuczogdGliYmxlIHdpdGggY29sdXVtbnMgaW5kZXggJiB2YWx1ZS4gaW5kZXggaGFzIG51bV9zYW1wbGVzIGVxdWFsbHkgc3BhY2VkIHBvaW50cyBmcm9tIDAgdG8gMQojIHZhbHVlcyBhcmUgaW50ZXJwbG9sYXRlZCBmcm9tIGYKZ2V0X2ludGVycG9sYXRlZF9kYXRhIDwtIGZ1bmN0aW9uKGRhdGEsIG51bV9zYW1wbGVzKSB7CiAgIyBBZGQgZW5kIHBvaW50cyBhdCAwIGFuZCAxIGlmIHRoZXkgZG9uJ3QgYWxyZWFkeSBleGlzdCwgZHVwbGljYXRpbmcgZmlyc3QgYW5kIGxhc3QgYXZhaWxhYmxlIGYgdmFsdWUKICBpZiAoISAwICVpbiUgZGF0YSRwZXJjZW50KSB7CiAgICBkYXRhIDwtIGFkZF9yb3coZGF0YSwgcGVyY2VudD0wLCBmPWRhdGEkZlsxXSwgc2NydWI9ZGF0YSRzY3J1YlsxXSwgLmJlZm9yZT0xKQogIH0KICBpZiAoISAoMSAlaW4lIGRhdGEkcGVyY2VudCkpIHsKICAgIGRhdGEgPC0gYWRkX3JvdyhkYXRhLCBwZXJjZW50PTEsIGY9dGFpbChkYXRhJGYsIDEpLCBzY3J1Yj10YWlsKGRhdGEkc2NydWIsIDEpKQogIH0KICAgICAgICAgIAogICMgQ3JlYXRlIGEgdGliYmxlIHdpdGggYWxsIHRoZSBwb2ludHMgd2UgYXJlIGludGVyZXN0ZWQgaW4sIHdpdGggTkEgdmFsdWVzCiAgc2FtcGxlX3BvaW50cyA8LSB0aWJibGUocGVyY2VudD1zZXEoMCwgMSwgbGVuZ3RoLm91dD1udW1fc2FtcGxlcyksIGY9TkEsIHNjcnViPU5BKSAKICAKICAjIEFkZCBhbGwgdGhlIHNhbXBsZSBwb2ludHMgd2hvc2Ugc2NydWIgdmFsdWUgZG9lc24ndCBhbHJlYWR5IGV4aXN0IGluIGRhdGEKICBkYXRhMiA8LSBiaW5kX3Jvd3MoZGF0YSwgZmlsdGVyKHNhbXBsZV9wb2ludHMsICEocGVyY2VudCAlaW4lIGRhdGEkcGVyY2VudCkpKSAgJT4lCiAgICAjIFRoZW4gc29ydCBieSBzY3J1YiBjb2x1bW5zICAKICAgIGFycmFuZ2UocGVyY2VudCkKICAKICAjIFRyYW5zZm9ybSBpbnRvIHpvbyBvYmplY3QgdG8gZmlsbCB0aGUgTkFzIHdpdGggaW50ZXJwb2xhdGVkIHZhbHVlcwogIHogPC0gcmVhZC56b28oZGF0YTIpICU+JSBuYS5hcHByb3ggJT4lCiAgIHRrX3RibChwcmVzZXJ2ZV9pbmRleD1UUlVFLCByZW5hbWVfaW5kZXg9ImluZGV4IikgJT4lCiAgIGZpbHRlcihpbmRleCAlaW4lIHNhbXBsZV9wb2ludHMkcGVyY2VudCkKICAKICByZXR1cm4oeikKfQoKc2V0d2QoIi4uLy4uLy4uLy4uL25vdGVib29rcy8iKQoKIyBBIHZlY3RvciB3aXRoIGp1c3QgdGhlIG5hbWVzIG9mIHRoZSBwaHJhc2VzICh3L28gLmpzb24gZXh0ZW5zaW9uKQpnZXN0cyA8LSBkby5jYWxsKCJyYmluZCIsIGxhcHBseShnZXN0X2ZpbGVuYW1lcywgZnVuY3Rpb24oZWx0KSB7CiAgc3RyX3NwbGl0KGVsdCwgJy5nZXN0JylbWzFdXVsxXQp9KSlbLDFdCgojIE9yaWdpbmFsIGdlc3R1cmVzCmdlc3Rfb3JpZ2luYWxzIDwtIGxhcHBseShnZXN0X2ZpbGVuYW1lcywgZnVuY3Rpb24oZWx0KSB7CiAgcmVzdWx0IDwtIGZyb21KU09OKHBhc3RlMChwYXRoLCBlbHQpKSAlPiUgYXNfdGliYmxlKCkgJT4lIAogICMgTk9URSBUSElTIEVYVFJBIFNURVAgICAgICAKICBwcmVwX2dlc3QoKQp9KQoKIyBSZXR1cm5zIHRoZSBpbmRleCBvZiB0aGUgZ2VzdHVyZQpnZXRfZ2VzdF9pbmRleCA8LSBmdW5jdGlvbihnZXN0X25hbWUpIHsKICBpbmRleCA8LSBtYXRjaChnZXN0X25hbWUsIGdlc3RzKQogIGlmIChpcy5uYShpbmRleCkpIHsgcmV0dXJuKE5VTEwpIH0KICByZXR1cm4oaW5kZXgpCn0KCiMgSW50ZXJwb2xhdGVkIHRpbWUgc2VyaWVzIGRhdGEgb2YgZiAmIHNjcnViIHZhbHVlCmdlc3RfaW50ZXJwcyA8LSBsYXBwbHkoZ2VzdHMsIGZ1bmN0aW9uKGVsdCkgewogICMgRWx0IGlzIHRoZSBuYW1lIG9mIGEgZ2VzdHVyZS4gV2Ugd2FudCB0byBmaW5kIGl0cyBpbmRleCBmaXJzdAogIGluZGV4PC1nZXRfZ2VzdF9pbmRleChlbHQpCiAgCiAgIyBJbiBvcmRlciB0byBnZXQgdGhlIGFjdHVhbCBnZXN0dXJlIGRhdGEKICBnZXN0X29yaWc8LWdlc3Rfb3JpZ2luYWxzW1tpbmRleF1dCiAgCiAgIyBHZXRzIG51bWJlciBvZiBzYW1wbGVzIGZvciB0aGUgZ2VzdCwgd2hpY2ggaXMgY29tcHV0ZWQgZnJvbSB0aGUgcmVmZXJlbmNlCiAgbnVtX3NhbXBsZXM8LWdldF9udW1fc2FtcGxlcyhlbHQpCiAgCiAgIyBEbyB0aGUgaW50ZXJwb2xhdGlvbiAgCiAgZ2VzdF9pbnRlcnA8LWdldF9pbnRlcnBvbGF0ZWRfZGF0YShnZXN0X29yaWcsIG51bV9zYW1wbGVzKQogIAogIHJldHVybihnZXN0X2ludGVycCkKfSkKCgpgYGAKCiMjIENvbXBhcmluZyB3aXRoIHJlZmVyZW5jZQpgYGB7cn0KIyBDb21wdXRlIHRoZSBjb3JyZWxhdGlvbiBhbmQgcm1zIGdpdmVuIHRoZSBlbnRpcmUgZ2VzdHVyZSBuYW1lCmdldF9jb3JyIDwtIGZ1bmN0aW9uKGdlc3RfbmFtZSwgd2VpZ2h0cz1GQUxTRSkgewogICMgR2V0IHRoZSBpbmRleCBvZiB0aGUgZ2VzdHVyZQogIGluZGV4PC1nZXRfZ2VzdF9pbmRleChnZXN0X25hbWUpCiAgIyBVc2UgaXQgdG8gZ2V0IHRoZSBmcmVxIG9mIHRoZSBnZXN0dXJlCiAgZ2VzdF9waXRjaCA8LSBnZXN0X2ludGVycHNbW2luZGV4XV0kZgogIAogICMgR2V0IHRoZSByZWZlcmVuY2UgcGhyYXNlCiAgcGhyYXNlX25hbWUgPC0gZ2V0X3JlZl9uYW1lKGdlc3RfbmFtZSkgICAgCiAgIyBVc2UgaXQgdG8gZ2V0IHRoZSBwcm9zb2dyYW0gcGl0Y2ggcmVmIGFuZCB0aGUgaW50ZW5zaXR5CiAgcHJvc29ncmFtX3BpdGNoIDwtIGdldF9yZWZfcHRzZihwaHJhc2VfbmFtZSkKICAKICAjIElmIHVzaW5nIHdlaWdodHMsIGdldCB0aGUgaW50ZW5zaXR5IG9mIHRoZSByZWYgcGhyYXNlCiAgaWYgKHdlaWdodHMpIHsgIAogICAgaW50ZW5zaXR5IDwtIGdldF9yZWZfdHNpKHBocmFzZV9uYW1lKQogIAogICAgd2MgPC0gd2VpZ2h0ZWRDb3JyKGdlc3RfcGl0Y2gsIHByb3NvZ3JhbV9waXRjaCwgbWV0aG9kID0gIlBlYXJzb24iLCAKICAgICAgICAgICAgICAgd2VpZ2h0cyA9IGludGVuc2l0eSwgTUwgPSBGQUxTRSwgZmFzdCA9IFRSVUUpCiAgICByZXR1cm4od2MpCiAgfSBlbHNlIHsKICAgIHJldHVybih3ZWlnaHRlZENvcnIoZ2VzdF9waXRjaCwgcHJvc29ncmFtX3BpdGNoLCBtZXRob2QgPSAiUGVhcnNvbiIsIAogICAgICAgICAgICAgICB3ZWlnaHRzID0gcmVwKDEsIGxlbmd0aChnZXN0X3BpdGNoKSksIE1MID0gRkFMU0UsIGZhc3QgPSBUUlVFKSkKICB9Cn0KCiMgR2V0IHRoZSB3ZWlnaHRlZCBjb3JyZWxhdGlvbgp3Y29ycl9hbGwgPC0gZG8uY2FsbCgicmJpbmQiLCBsYXBwbHkoZ2VzdHMsIGZ1bmN0aW9uKGVsdCkgewogIGdldF9jb3JyKGVsdCwgd2VpZ2h0cz1UUlVFKQp9KSlbLDFdCiMgQW5kIHRoZSBub3JtYWwgY29ycmVsYXRpb24ganVzdCBmb3IgY29tcGFyaXNvbgpjb3JyX2FsbCA8LSBkby5jYWxsKCJyYmluZCIsIGxhcHBseShnZXN0cywgZnVuY3Rpb24oZWx0KSB7CiAgZ2V0X2NvcnIoZWx0KQp9KSlbLDFdCgpnZXN0X3Njb3JlcyA8LSB0aWJibGUoZ2VzdD1nZXN0cywgd2NvcnI9d2NvcnJfYWxsLCBjb3JyPWNvcnJfYWxsKSAlPiUKICAgICAgICAgICAgICAgIG11dGF0ZShjb3JyX2RpZmY9d2NvcnItY29ycikKcHJpbnQoYXMubWF0cml4KGdlc3Rfc2NvcmVzKSwgcXVvdGUgPSBGQUxTRSkKYGBgClRoZXJlIGRvZXNuJ3Qgc2VlbSB0byBiZSBhIGh1Z2UgYW1vdW50IG9mIGRpZmZlcmVuY2Ugd2hlbiBpbnRlbnNpdHkgaXMgdGFrZW4gaW50byBhY2NvdW50LiBJJ20gbm90IHN1cmUgdGhhdCB0aGUgd2VpZ2h0ZWQgY29ycmVsYXRpb24gaXMgbW9yZSBhY2N1cmF0ZS4KCmBgYHtyfQptZWFuKGdlc3Rfc2NvcmVzJGNvcnJfZGlmZikKYGBgCmBgYHtyfQptYXgoZ2VzdF9zY29yZXMkY29ycl9kaWZmKQpgYGAKYGBge3J9Cm1pbihnZXN0X3Njb3JlcyRjb3JyX2RpZmYpCmBgYApgYGB7cn0Kc2QoZ2VzdF9zY29yZXMkY29ycl9kaWZmKQpgYGAKCkp1c3QgZm9yIGNvbXBsZXRlbmVzcywgbGV0J3MgbWFrZSBzb21lIHBsb3RzLi4uCmBgYHtyfQpwbG90X2dlc3RfYW5kX3JlZiA8LSBmdW5jdGlvbihnZXN0X25hbWUpIHsKICAjIEdldCB0aGUgaW5kZXggb2YgdGhlIGdlc3R1cmUKICBpbmRleDwtZ2V0X2dlc3RfaW5kZXgoZ2VzdF9uYW1lKQogICMgVXNlIGl0IHRvIGdldCB0aGUgZnJlcSBvZiB0aGUgZ2VzdHVyZQogIGdlc3RfcGl0Y2ggPC0gZ2VzdF9pbnRlcnBzW1tpbmRleF1dJGYKICAKICAjIEdldCB0aGUgcmVmZXJlbmNlIHBocmFzZQogIHBocmFzZV9uYW1lIDwtIGdldF9yZWZfbmFtZShnZXN0X25hbWUpICAgIAogICMgVXNlIGl0IHRvIGdldCB0aGUgcHJvc29ncmFtIHBpdGNoIHJlZiBhbmQgdGhlIGludGVuc2l0eQogIHByb3NvZ3JhbV9waXRjaCA8LSBnZXRfcmVmX3B0c2YocGhyYXNlX25hbWUpCiAgaW50ZW5zaXR5IDwtIGdldF9yZWZfdHNpKHBocmFzZV9uYW1lKQogIAogIHJvdyA8LSBmaWx0ZXIoZ2VzdF9zY29yZXMsIGdlc3Q9PWdlc3RfbmFtZSkKICB3Y29yciA8LSByb3VuZChyb3ckd2NvcnIsIDQpCiAgY29yciA8LSByb3VuZChyb3ckY29yciwgNCkKICAKICBteV9kYXRhIDwtIHRpYmJsZShnZXN0PWdlc3RfcGl0Y2gsIHJlZj1wcm9zb2dyYW1fcGl0Y2gsIGk9aW50ZW5zaXR5LCAKICAgICAgICAgICAgICAgICAgICBwZXJjZW50PWdlc3RfaW50ZXJwc1tbaW5kZXhdXSRpbmRleCkKICAKICBteV9wbG90IDwtIGdncGxvdChkYXRhPW15X2RhdGEsIGFlcyh4PXBlcmNlbnQsIHk9Z2VzdCwgY29sb3I9Imdlc3R1cmUiKSkgKwogICAgc2NhbGVfY29sb3JfZGlzY3JldGUobmFtZSA9ICJEYXRhIHNvdXJjZSIpICsKICAgIGdlb21fbGluZSgpICsKICAgIGdlb21fbGluZShkYXRhPW15X2RhdGEsIGFlcyh4PXBlcmNlbnQsIHk9cmVmLCBjb2xvcj0icmVmZXJlbmNlIikpICsKICAgIGdlb21fbGluZShkYXRhPW15X2RhdGEsIGFlcyh4PXBlcmNlbnQsIHk9aSwgY29sb3I9ImludGVuc2l0eSIpKSArCiAgICBsYWJzKHRpdGxlPXBhc3RlKGdlc3RfbmFtZSwgIndpdGggaXRzIHJlZmVyZW5jZSIpLCAKICAgICAgICAgICAgc3VidGl0bGU9cGFzdGUoIldlaWdodGVkIENvcnJlbGF0aW9uOiIsIHdjb3JyLCAiTm9uLXdlaWdodGVkOiIsIGNvcnIpLAogICAgICAgICAgICB5PSJmcmVxdWVuY3kgKHNlbWl0b25lcyBmcm9tIDEzMGh6KSBcbnVuc3BlY2lmaWVkIGZvciBpbnRlbnNpdHkiLAogICAgICAgICAgICB4PSJwZXJjZW50IHRpbWUiKQogIHJldHVybihteV9wbG90KQp9CgpgYGAKCgojIENvbXBhcmlzb24gb2Ygc2NydWIKCiMjIENvcnJlbGF0aW9uCgpTY3J1YiBzaG91bGQgZ28gYXMgbGluZWFybHkgYXMgcG9zc2libGUsIHNvIHdlIGNhbiBjb21wYXJlIGl0IHdpdGggdGhlIGluZGV4IChwZXJjZW50IHRpbWUpLgpGaXJzdCBsb29rIGF0IGNvcnJlbGF0aW9uLgoKYGBge3J9CgojIEdldCB0aGUgY29ycmVsYXRpb24gb2YgdGhlIHNjcnViCmdldF9jb3JyX3RpbWluZyA8LSBmdW5jdGlvbihnZXN0X25hbWUsIHdlaWdodHM9RkFMU0UpIHsKICAjIEdldCB0aGUgaW5kZXggb2YgdGhlIGdlc3R1cmUKICBpbmRleDwtZ2V0X2dlc3RfaW5kZXgoZ2VzdF9uYW1lKQogICMgVXNlIGl0IHRvIGdldCB0aGUgc2NydWIKICBnZXN0X3NjcnViIDwtIGdlc3RfaW50ZXJwc1tbaW5kZXhdXSRzY3J1YgogICMgVXNlIGl0IHRvIGdldCB0aGUgcGVyY2VudCB0aW1lIChpbmRleCkKICBnZXN0X3RpbWUgPC0gZ2VzdF9pbnRlcnBzW1tpbmRleF1dJGluZGV4CiAgCiAgIyBHZXQgdGhlIHJlZmVyZW5jZSBwaHJhc2UKICBwaHJhc2VfbmFtZSA8LSBnZXRfcmVmX25hbWUoZ2VzdF9uYW1lKSAgICAKCiAgIyBJZiB1c2luZyB3ZWlnaHRzLCBnZXQgdGhlIGludGVuc2l0eSBvZiB0aGUgcmVmIHBocmFzZQogIGlmICh3ZWlnaHRzKSB7ICAKICAgIGludGVuc2l0eSA8LSBnZXRfcmVmX3RzaShwaHJhc2VfbmFtZSkKICAKICAgIHdjIDwtIHdlaWdodGVkQ29ycihnZXN0X3RpbWUsIGdlc3Rfc2NydWIsIG1ldGhvZCA9ICJQZWFyc29uIiwgCiAgICAgICAgICAgICAgIHdlaWdodHMgPSBpbnRlbnNpdHksIE1MID0gRkFMU0UsIGZhc3QgPSBUUlVFKQogICAgcmV0dXJuKHdjKQogIH0gZWxzZSB7CiAgICByZXR1cm4oY29yKGdlc3RfdGltZSwgZ2VzdF9zY3J1YiwgbWV0aG9kPSJwZWFyc29uIikpCiAgfQp9CgojIEdldCB0aGUgY29ycmVsYXRpb24uIEl0J3MgdW53ZWlnaHRlZCBiZWNhdXNlIGl0J3MgdGltZSwgbm90IGZyZXF1ZW5jeS4KY29ycl9hbGxfdGltaW5nIDwtIGRvLmNhbGwoInJiaW5kIiwgbGFwcGx5KGdlc3RzLCBmdW5jdGlvbihlbHQpIHsKICBnZXRfY29ycl90aW1pbmcoZWx0KQp9KSlbLDFdCgpnZXN0X3Njb3Jlc190aW1pbmcgPC0gdGliYmxlKGdlc3Q9Z2VzdHMsIGNvcnJfdGltaW5nPWNvcnJfYWxsX3RpbWluZykKcHJpbnQoYXMubWF0cml4KGdlc3Rfc2NvcmVzX3RpbWluZyksIHF1b3RlPUZBTFNFKQoKYGBgCgpUaGV5IGFyZSBhbGwgdmVyeSBoaWdoLCBidXQgd2l0aGluIHRoYXQgcmFuZ2Ugd2UgY2FuIHN0aWxsIGNoZWNrIG91dCB0aGUgZ2VzdGVzIHdpdGggbG93ZXIgc2NvcmUuIDIzIGFuZCAxNyBhcmUgdGhlIG9ubHkgb25lcyB0aGF0IGFyZSBsb3dlciB0aGFuIC45OC4KCioqIDIzICoqCkdlc3R1cmUgPGh0bWw+PGF1ZGlvIGNvbnRyb2xzPgo8c291cmNlIHNyYz0iLi4vZGF0YS8yMF8xMl8xNS1waWxvdF9nZXN0dXJlcy9hbWJpZ3VvdXMvYXVkaW8vMjdiaXNfbGFfYm9ubmVfY3Vpc2luZV9fYXZlY19kZXNfbmF2ZXRzLW0yLm1wMyIgdHlwZT0iYXVkaW8vbXAzIj4KPC9hdWRpbz48L2h0bWw+CgpPcmlnaW5hbCA8aHRtbD48YXVkaW8gY29udHJvbHM+Cjxzb3VyY2Ugc3JjPSIuLi9kYXRhLzIwXzEyXzE1LXBpbG90X2dlc3R1cmVzL2FtYmlndW91cy9hdWRpby8yN2Jpc19sYV9ib25uZV9jdWlzaW5lX19hdmVjX2Rlc19uYXZldHMubXAzIiB0eXBlPSJhdWRpby9tcDMiPgo8L2F1ZGlvPjwvaHRtbD4KCjxici8+CgoqKiAxNyAqKgpHZXN0dXJlIDxodG1sPjxhdWRpbyBjb250cm9scz4KPHNvdXJjZSBzcmM9Ii4uL2RhdGEvMjBfMTJfMTUtcGlsb3RfZ2VzdHVyZXMvYW1iaWd1b3VzL2F1ZGlvLzIwYmlzX2plYW5waWVycmVfZXRfamFjcXVlcy15MS5tcDMiIHR5cGU9ImF1ZGlvL21wMyI+CjwvYXVkaW8+PC9odG1sPgoKT3JpZ2luYWwgPGh0bWw+PGF1ZGlvIGNvbnRyb2xzPgo8c291cmNlIHNyYz0iLi4vZGF0YS8yMF8xMl8xNS1waWxvdF9nZXN0dXJlcy9hbWJpZ3VvdXMvYXVkaW8vMjBiaXNfamVhbnBpZXJyZV9ldF9qYWNxdWVzLm1wMyIgdHlwZT0iYXVkaW8vbXAzIj4KPC9hdWRpbz48L2h0bWw+CgpUaGVzZSBkZWZpbml0ZWx5IGhhdmUgbW9yZSBwcm9ibGVtYXRpYyByaHl0aG1zLiBCdXQgaXQncyByZWFsbHkgaGFyZCB0byBkcmF3IHRoZSBsaW5lIG9uIHdoYXQgaXMgZ29vZCBhbmQgd2hhdCBpcyBiYWQuCgojIyBEaXN0YW5jZQoKTGV0J3MgdHJ5IGFub3RoZXIgbWVhc3VyZW1lbnQuIENvcnJlbGF0aW9uIGFuZCBSTVNFIHN1YnRyYWN0cyB0aGUgbWVhbiwgYnV0IHRoZSBtZWFuIGRvZXNuJ3QgaGF2ZSBtdWNoIHNlbnNlIHdoZW4gd2UgdGFsayBhYm91dCB0aW1pbmcuIFNvIGxldCdzIGp1c3Qgc3VtIHVwIGFsbCB0aGUgZGlzdGFuY2UgYmV0d2VlbiBhbGwgdGhlIHBvaW50cy4gQSBoaWdoZXIgbnVtYmVyIG1lYW5zIGxlc3MgYWNjdXJhdGUgdGltaW5nLiAKCk9uZSBpc3N1ZSBpcyB0aGF0IGxvbmdlciBwaHJhc2VzIGhhdmUgbW9yZSBwb2ludHMuIFRoaXMgaXMgcHJvYmxlbWF0aWMgYmVjYXVzZSBsb25nZXIgcGhyYXNlcyB3aWxsIGdlbmVyYWxseSBoYXZlIGEgbGFyZ2VyIHRvdGFsIGRpc3RhbmNlLiBXZSBjYW4gdHJ5IHRvIG1pdGlnYXRlIHRoYXQgYnkgdGFraW5nIHRoZSBhdmVyYWdlLCBidXQgdGhhdCdzIGFsc28gcHJvYmxlbWF0aWMgYmVjYXVzZSBhIGxvbmdlciBwaHJhc2Ugd2l0aCBpbmFjY3VyYXRlIHRpbWluZyBpbiBvbmUgcGxhY2Ugd2lsbCBub3QgbmVjZXNzYXJpbHkgc2hvdy4KClNvIHdlIGhhdmUgdG8gcmVzYW1wbGUgdGhlIGdlc3R1cmVzIHN1Y2ggdGhhdCB0aGV5IGFsbCBoYXZlIHRoZSBzYW1lIG51bWJlciBvZiBwb2ludHMuCgpgYGB7cn0KCiMgSW50ZXJwb2xhdGVkIHRpbWUgc2VyaWVzIGRhdGEKbnVtX3NhbXBsZXMgPC0gMTAwCmdlc3RfaW50ZXJwczIgPC0gbGFwcGx5KGdlc3RzLCBmdW5jdGlvbihlbHQpIHsKICAjIEVsdCBpcyB0aGUgbmFtZSBvZiBhIGdlc3R1cmUuIFdlIHdhbnQgdG8gZmluZCBpdHMgaW5kZXggZmlyc3QKICBpbmRleDwtZ2V0X2dlc3RfaW5kZXgoZWx0KQogIAogICMgSW4gb3JkZXIgdG8gZ2V0IHRoZSBhY3R1YWwgZ2VzdHVyZSBkYXRhCiAgZ2VzdF9vcmlnPC1nZXN0X29yaWdpbmFsc1tbaW5kZXhdXQogIAogICMgRG8gdGhlIGludGVycG9sYXRpb24gIAogIGdlc3RfaW50ZXJwPC1nZXRfaW50ZXJwb2xhdGVkX2RhdGEoZ2VzdF9vcmlnLCBudW1fc2FtcGxlcykKICAKICByZXR1cm4oZ2VzdF9pbnRlcnApCn0pCgoKZGlzdCA8LSBmdW5jdGlvbihhLCBiKSB7CiAgcmV0dXJuKHNxcnQoc3VtKCAoYSAtIGIpIF4yICkpKQp9CgpnZXRfZGlzdF90aW1pbmcgPC0gZnVuY3Rpb24oZ2VzdF9uYW1lKSB7CiAgIyBHZXQgdGhlIGluZGV4IG9mIHRoZSBnZXN0dXJlCiAgaW5kZXg8LWdldF9nZXN0X2luZGV4KGdlc3RfbmFtZSkKICAjIFVzZSBpdCB0byBnZXQgdGhlIHNjcnViCiAgZ2VzdF9zY3J1YiA8LSBnZXN0X2ludGVycHMyW1tpbmRleF1dJHNjcnViCiAgIyBVc2UgaXQgdG8gZ2V0IHRoZSBwZXJjZW50IHRpbWUgKGluZGV4KQogIGdlc3RfdGltZSA8LSBnZXN0X2ludGVycHMyW1tpbmRleF1dJGluZGV4CiAgCiAgcmV0dXJuKGRpc3QoZ2VzdF9zY3J1YiwgZ2VzdF90aW1lKSkKfQoKIyBHZXQgdGhlIHRpbWluZyBybXNlIGZvciBhbGwgZ2VzdHVyZXMKZGlzdF9hbGxfdGltaW5nIDwtIGRvLmNhbGwoInJiaW5kIiwgbGFwcGx5KGdlc3RzLCBmdW5jdGlvbihlbHQpIHsKICBnZXRfZGlzdF90aW1pbmcoZWx0KQp9KSlbLDFdCgoKZ2VzdF9zY29yZXNfdGltaW5nMiA8LSB0aWJibGUoZ2VzdD1nZXN0cywgY29ycl90aW1pbmc9Y29ycl9hbGxfdGltaW5nLCBkaXN0X3RpbWluZz1kaXN0X2FsbF90aW1pbmcpCnByaW50KGFzLm1hdHJpeChnZXN0X3Njb3Jlc190aW1pbmcyKSwgcXVvdGU9RkFMU0UpCgpgYGAKCmBgYHtyfQpwbG90X2dlc3RfYW5kX3JlZl90aW1pbmcgPC0gZnVuY3Rpb24oZ2VzdF9uYW1lKSB7CiAgIyBHZXQgdGhlIGluZGV4IG9mIHRoZSBnZXN0dXJlCiAgaW5kZXg8LWdldF9nZXN0X2luZGV4KGdlc3RfbmFtZSkKICAjIFVzZSBpdCB0byBnZXQgdGhlIGZyZXEgb2YgdGhlIGdlc3R1cmUKICBnZXN0X3NjcnViIDwtIGdlc3RfaW50ZXJwc1tbaW5kZXhdXSRzY3J1YgogIGdlc3RfaW5kZXggPC0gZ2VzdF9pbnRlcnBzW1tpbmRleF1dJGluZGV4CgogIHJvdyA8LSBmaWx0ZXIoZ2VzdF9zY29yZXNfdGltaW5nMiwgZ2VzdD09Z2VzdF9uYW1lKQogIGNvcnIgPC0gcm91bmQocm93JGNvcnJfdGltaW5nLCA0KQogIGRpc3QgPC0gcm91bmQocm93JGRpc3RfdGltaW5nLCA0KQogIAogIG15X2RhdGEgPC0gdGliYmxlKGdlc3Q9Z2VzdF9zY3J1YiwgcmVmPWdlc3RfaW5kZXgpCiAgCiAgbXlfcGxvdCA8LSBnZ3Bsb3QoZGF0YT1teV9kYXRhLCBhZXMoeD1yZWYsIHk9Z2VzdCwgY29sb3I9Imdlc3R1cmUiKSkgKwogICAgc2NhbGVfY29sb3JfZGlzY3JldGUobmFtZSA9ICJEYXRhIHNvdXJjZSIpICsKICAgIGdlb21fbGluZSgpICsKICAgIGdlb21fbGluZShkYXRhPW15X2RhdGEsIGFlcyh4PXJlZiwgeT1yZWYsIGNvbG9yPSJpZGVhbCIpKSArCiAgICBsYWJzKHRpdGxlPXBhc3RlKCJUaW1pbmcgb2YiLCBnZXN0X25hbWUsICJjb21wYXJlZCB3aXRoIGlkZWFsIGxpbmVhciB0aW1lIiksIAogICAgICAgICAgICBzdWJ0aXRsZT1wYXN0ZSgiQ29ycmVsYXRpb246IiwgY29yciwgIkRpc3RhbmNlOiIsIGRpc3QpLAogICAgICAgICAgICB5PSJTY3J1YiBwZXJjZW50YWdlIiwKICAgICAgICAgICAgeD0icGVyY2VudCB0aW1lIikKICByZXR1cm4obXlfcGxvdCkKfQpgYGAKCiMjIEJlc3QgdGltaW5nIHNjb3JlcyAoPDAuNSkKCmBgYHtyfQpwbG90X2dlc3RfYW5kX3JlZl90aW1pbmcoZ2VzdHNbWzIyXV0pCnBsb3RfZ2VzdF9hbmRfcmVmKGdlc3RzW1syMl1dKQpgYGAKCkdlc3R1cmUgPGh0bWw+PGF1ZGlvIGNvbnRyb2xzPgo8c291cmNlIHNyYz0iLi4vZGF0YS8yMF8xMl8xNS1waWxvdF9nZXN0dXJlcy9hbWJpZ3VvdXMvYXVkaW8vMjdiaXNfbGFfYm9ubmVfY3Vpc2luZV9fYXZlY19kZXNfbmF2ZXRzLW0xLm1wMyIgdHlwZT0iYXVkaW8vbXAzIj4KPC9hdWRpbz48L2h0bWw+CgpPcmlnaW5hbCA8aHRtbD48YXVkaW8gY29udHJvbHM+Cjxzb3VyY2Ugc3JjPSIuLi9kYXRhLzIwXzEyXzE1LXBpbG90X2dlc3R1cmVzL2FtYmlndW91cy9hdWRpby8yN2Jpc19sYV9ib25uZV9jdWlzaW5lX19hdmVjX2Rlc19uYXZldHMubXAzIiB0eXBlPSJhdWRpby9tcDMiPgo8L2F1ZGlvPjwvaHRtbD4KClRoaXMgaXMgYWN0dWFsbHkgcXVpdGUgZ29vZC4gVGhlIHBpdGNoIGN1cnZlIGNhbiBiZSBjbG9zZXIgdG8gdGhlIG9yaWdpbmFsLCBidXQgaXQgaGFzIHRoZSByaWdodCBvdmVyYWxsIGNvbnRvdXIuCgo8YnIvPgoKYGBge3J9CnBsb3RfZ2VzdF9hbmRfcmVmX3RpbWluZyhnZXN0c1tbM11dKQpwbG90X2dlc3RfYW5kX3JlZihnZXN0c1tbM11dKQpgYGAKCkdlc3R1cmUgPGh0bWw+PGF1ZGlvIGNvbnRyb2xzPgo8c291cmNlIHNyYz0iLi4vZGF0YS8yMF8xMl8xNS1waWxvdF9nZXN0dXJlcy9hbWJpZ3VvdXMvYXVkaW8vMTBfamVhbl9zYWlnbmVfYmVhdWNvdXAtYV9tLm1wMyIgdHlwZT0iYXVkaW8vbXAzIj4KPC9hdWRpbz48L2h0bWw+CgpPcmlnaW5hbCA8aHRtbD48YXVkaW8gY29udHJvbHM+Cjxzb3VyY2Ugc3JjPSIuLi9kYXRhLzIwXzEyXzE1LXBpbG90X2dlc3R1cmVzL2FtYmlndW91cy9hdWRpby8xMF9qZWFuX3NhaWduZV9iZWF1Y291cC5tcDMiIHR5cGU9ImF1ZGlvL21wMyI+CjwvYXVkaW8+PC9odG1sPgoKRXZlbiB0aG91Z2ggdGhlIHRpbWluZyBmb2xsb3dzIHZlcnkgY2xvc2VseSB0aGUgbGluZWFyIGlkZWEsIHRoaXMgZG9lcyBub3Qgc291bmQgcmlnaHQgYmVjYXVzZSB0aGUgZnJlcXVlbmN5IGN1cnZlIGlzIGluY29ycmVjdC4KCjxici8+CgojIyBRdWl0ZSBnb29kIHNjb3JlcyAoPDAuMSkKCmBgYHtyLCBmaWcua2VlcD0nYWxsJ30KcGxvdF9nZXN0X2FuZF9yZWZfdGltaW5nKGdlc3RzW1s3XV0pCnBsb3RfZ2VzdF9hbmRfcmVmKGdlc3RzW1s3XV0pCmBgYAoKR2VzdHVyZSA8aHRtbD48YXVkaW8gY29udHJvbHM+Cjxzb3VyY2Ugc3JjPSIuLi9kYXRhLzIwXzEyXzE1LXBpbG90X2dlc3R1cmVzL2FtYmlndW91cy9hdWRpby8xM19sYV9iZWxsZV9mZXJtZV9sZV92b2lsZS1jMi5tcDMiIHR5cGU9ImF1ZGlvL21wMyI+CjwvYXVkaW8+PC9odG1sPgoKT3JpZ2luYWwgPGh0bWw+PGF1ZGlvIGNvbnRyb2xzPgo8c291cmNlIHNyYz0iLi4vZGF0YS8yMF8xMl8xNS1waWxvdF9nZXN0dXJlcy9hbWJpZ3VvdXMvYXVkaW8vMTNfbGFfYmVsbGVfZmVybWVfbGVfdm9pbGUubXAzIiB0eXBlPSJhdWRpby9tcDMiPgo8L2F1ZGlvPjwvaHRtbD4KClRoaXMgaXMgYSB2ZXJ5IGdvb2QgZ2VzdHVyZS4gVGhlIHRpbWluZyBpcyBub3QgZXhhY3RseSBsaW5lYXIsIGJ1dCBpdCdzIGNsb3NlIGVub3VnaC4KCjxici8+CgoKYGBge3IsIGZpZy5rZWVwPSdhbGwnfQpwbG90X2dlc3RfYW5kX3JlZl90aW1pbmcoZ2VzdHNbWzldXSkKcGxvdF9nZXN0X2FuZF9yZWYoZ2VzdHNbWzldXSkKYGBgCgpHZXN0dXJlIDxodG1sPjxhdWRpbyBjb250cm9scz4KPHNvdXJjZSBzcmM9Ii4uL2RhdGEvMjBfMTJfMTUtcGlsb3RfZ2VzdHVyZXMvYW1iaWd1b3VzL2F1ZGlvLzEzYmlzX2xhX2JlbGxlX2Zlcm1lX19sZV92b2lsZS1nMi5tcDMiIHR5cGU9ImF1ZGlvL21wMyI+CjwvYXVkaW8+PC9odG1sPgoKT3JpZ2luYWwgPGh0bWw+PGF1ZGlvIGNvbnRyb2xzPgo8c291cmNlIHNyYz0iLi4vZGF0YS8yMF8xMl8xNS1waWxvdF9nZXN0dXJlcy9hbWJpZ3VvdXMvYXVkaW8vMTNiaXNfbGFfYmVsbGVfZmVybWVfX2xlX3ZvaWxlLm1wMyIgdHlwZT0iYXVkaW8vbXAzIj4KPC9hdWRpbz48L2h0bWw+CgpUaGlzIGlzIGFsc28gYSB2ZXJ5IGdvb2QgZ2VzdHVyZSBmb3IgYm90aCBmcmVxdWVuY3kgYW5kIHRpbWluZy4gVGltaW5nIGlzIG5vdCBwZXJmZWN0LCBvbmNlIGFnYWluLCBidXQgaXQncyBnb29kIGVub3VnaC4KCjxici8+CgpgYGB7ciwgZmlnLmtlZXA9J2FsbCd9CnBsb3RfZ2VzdF9hbmRfcmVmX3RpbWluZyhnZXN0c1tbMjBdXSkKcGxvdF9nZXN0X2FuZF9yZWYoZ2VzdHNbWzIwXV0pCmBgYAoKR2VzdHVyZSA8aHRtbD48YXVkaW8gY29udHJvbHM+Cjxzb3VyY2Ugc3JjPSIuLi9kYXRhLzIwXzEyXzE1LXBpbG90X2dlc3R1cmVzL2FtYmlndW91cy9hdWRpby8yN19sYV9ib25uZV9fY3Vpc2luZV9hdmVjX2Rlc19uYXZldHMtdjIubXAzIiB0eXBlPSJhdWRpby9tcDMiPgo8L2F1ZGlvPjwvaHRtbD4KCk9yaWdpbmFsIDxodG1sPjxhdWRpbyBjb250cm9scz4KPHNvdXJjZSBzcmM9Ii4uL2RhdGEvMjBfMTJfMTUtcGlsb3RfZ2VzdHVyZXMvYW1iaWd1b3VzL2F1ZGlvLzI3X2xhX2Jvbm5lX19jdWlzaW5lX2F2ZWNfZGVzX25hdmV0cy5tcDMiIHR5cGU9ImF1ZGlvL21wMyI+CjwvYXVkaW8+PC9odG1sPgoKVGhpcyBzb3VuZHMgYSBiaXQgZXhhZ2dlcmF0ZWQsIGJ1dCBpdCdzIHN0aWxsIGRlZmluaXRlbHkgY29ycmVjdC4KCjxici8+CgojIyBXb3JzdCB0aW1pbmcgc2NvcmVzICg+MSkKCmBgYHtyLCBmaWcua2VlcD0nYWxsJ30KcGxvdF9nZXN0X2FuZF9yZWZfdGltaW5nKGdlc3RzW1sxN11dKQpwbG90X2dlc3RfYW5kX3JlZihnZXN0c1tbMTddXSkKYGBgCgpHZXN0dXJlIDxodG1sPjxhdWRpbyBjb250cm9scz4KPHNvdXJjZSBzcmM9Ii4uL2RhdGEvMjBfMTJfMTUtcGlsb3RfZ2VzdHVyZXMvYW1iaWd1b3VzL2F1ZGlvLzIwYmlzX2plYW5waWVycmVfZXRfamFjcXVlcy15MS5tcDMiIHR5cGU9ImF1ZGlvL21wMyI+CjwvYXVkaW8+PC9odG1sPgoKT3JpZ2luYWwgPGh0bWw+PGF1ZGlvIGNvbnRyb2xzPgo8c291cmNlIHNyYz0iLi4vZGF0YS8yMF8xMl8xNS1waWxvdF9nZXN0dXJlcy9hbWJpZ3VvdXMvYXVkaW8vMjBiaXNfamVhbnBpZXJyZV9ldF9qYWNxdWVzLm1wMyIgdHlwZT0iYXVkaW8vbXAzIj4KPC9hdWRpbz48L2h0bWw+CgpUaGlzIGdlc3R1cmUgZG9lcyBub3Qgc2NvcmUgd2VsbCBmb3IgZWl0aGVyIHBpdGNoIG9yIHRpbWluZy4KCjxici8+CgpgYGB7ciwgZmlnLmtlZXA9J2FsbCd9CnBsb3RfZ2VzdF9hbmRfcmVmX3RpbWluZyhnZXN0c1tbMTFdXSkKcGxvdF9nZXN0X2FuZF9yZWYoZ2VzdHNbWzExXV0pCmBgYAoKR2VzdHVyZSA8aHRtbD48YXVkaW8gY29udHJvbHM+Cjxzb3VyY2Ugc3JjPSIuLi9kYXRhLzIwXzEyXzE1LXBpbG90X2dlc3R1cmVzL2FtYmlndW91cy9hdWRpby8xYmlzX0lsX25haXRyYWl0X3ByZW1hdHVyZS1iX2cubXAzIiB0eXBlPSJhdWRpby9tcDMiPgo8L2F1ZGlvPjwvaHRtbD4KCk9yaWdpbmFsIDxodG1sPjxhdWRpbyBjb250cm9scz4KPHNvdXJjZSBzcmM9Ii4uL2RhdGEvMjBfMTJfMTUtcGlsb3RfZ2VzdHVyZXMvYW1iaWd1b3VzL2F1ZGlvLzFiaXNfSWxfbmFpdHJhaXRfcHJlbWF0dXJlLm1wMyIgdHlwZT0iYXVkaW8vbXAzIj4KPC9hdWRpbz48L2h0bWw+CgpXaGlsZSB0aGlzIG9uZSBoYXMgYSB2ZXJ5IGdvb2Qgc2NvcmUgZm9yIHBpdGNoLCB0YWtpbmcgdGhlIHRpbWluZyBzY29yZSBpbnRvIGFjY291bnQgZ2l2ZXMgYSBzY29yZSB0aGF0IG1vcmUgYWNjdXJhdGVseSByZWZsZWN0IHRoZSBzdHJhbmdlbmVzcyB0aGF0IEkgaGVhci4KCjxici8+CgpgYGB7ciwgZmlnLmtlZXA9J2FsbCd9CnBsb3RfZ2VzdF9hbmRfcmVmX3RpbWluZyhnZXN0c1tbMjVdXSkKcGxvdF9nZXN0X2FuZF9yZWYoZ2VzdHNbWzI1XV0pCmBgYAoKR2VzdHVyZSA8aHRtbD48YXVkaW8gY29udHJvbHM+Cjxzb3VyY2Ugc3JjPSIuLi9kYXRhLzIwXzEyXzE1LXBpbG90X2dlc3R1cmVzL2FtYmlndW91cy9hdWRpby8yYmlzX3R1X3BhcmFpdHJhaXNfc291Y2lldXgtYV9nLm1wMyIgdHlwZT0iYXVkaW8vbXAzIj4KPC9hdWRpbz48L2h0bWw+CgpPcmlnaW5hbCA8aHRtbD48YXVkaW8gY29udHJvbHM+Cjxzb3VyY2Ugc3JjPSIuLi9kYXRhLzIwXzEyXzE1LXBpbG90X2dlc3R1cmVzL2FtYmlndW91cy9hdWRpby8yYmlzX3R1X3BhcmFpdHJhaXNfc291Y2lldXgubXAzIiB0eXBlPSJhdWRpby9tcDMiPgo8L2F1ZGlvPjwvaHRtbD4KClRoaXMgaGFzIGFuIG9rIHNjb3JlIGZvciBwaXRjaCwgYnV0IHRoZSB0aW1pbmcgc2NvcmUgcmVmbGVjdHMgaG93IHdlaXJkbHkgaXQgc291bmRzLgoKPGJyLz4=